<?php
define ('_FILE_CACHE', true);
/**
 * 文件缓存
 * 支持：变量名-缓存内容 每个变量对应多个tag
 * 缓存过期处理，tag过期，则缓存自动过期
 * 分目录hash存储
 *
 * @author RD
 */
class FileCache {
    /**
     * 缓存文件存放路径
     * 最后不加/ \
     */
    private $_cacheDir;

    private $_cc = array ();

    /**
     * 初始化
     */
    public function __construct ($cacheDir) {
        $this->_cacheDir = $cacheDir;
    }
    
    
    /**
     * 缓存
     *
     * @param $key 键名
     * @param $value 值
     * @param $expireTime 过期时间，0为不过期
     * @param $tags 标签
     */
    public function set ($key, $value, $expireTime = 0, array $tags = array ()) {
        $hash = $this->dirHash ($key);
        
        if ( !file_exists ($this->_cacheDir . '/' . $hash ['dir1']))
            mkdir ($this->_cacheDir . '/' . $hash ['dir1']);
        
        if ( !file_exists ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2']))
            mkdir ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2']);
        $t = time ();
        
        $arr = array ('cache'       => serialize ($value),
                      'expireTime'  => ($expireTime >0 && $expireTime < $t) ? $t + $expireTime : $expireTime,
                     );
        
        $c = "<?php\r\nif (!defined ('_FILE_CACHE')) exit;\r\n\$_FILE_CACHE_" . $key . '=' . var_export ($arr, true) . ';';
        
        file_put_contents ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2'] . '/' . $hash ['filename'], $c);
        
        $this->_cc [$key] = $value;
        if (count ($tags) > 0) {
            while (list (, $v) = each ($tags)) {
                if ($this->get ('tag_' . $v) == NULL) {
                    $this->set ('tag_' . $v, array ($key));
                }else {
                    $o      = $this->get ('tag_' . $v);
                    if (!in_array ($key, $o)) {
                        $o[]    = $key;
                        $this->set ('tag_' . $v, $o);
                    }
                }
            }
        }
    }
    
    
    /**
     * 获取缓存，若不存在，或者过期则返回null
     */
    public function get ($key) {
        if (isset ($this->_cc [$key]))
            return $this->_cc [$key];
        
        $hash = $this->dirHash ($key);
        
        if (file_exists ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2'] . '/' . $hash ['filename'])) {
            include $this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2'] . '/' . $hash ['filename'];
            $varkey = '_FILE_CACHE_' . $key;
            $_c = &$$varkey;
            if ($_c ['expireTime'] == 0 || $_c ['expireTime'] > time ()) {
                return unserialize ($_c ['cache']);
            }
        }
        
        return NULL;
    }
    
    
    public function delete ($key) {
        $hash = $this->dirHash ($key);
        if (file_exists ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2'] . '/' . $hash ['filename']))
            unlink ($this->_cacheDir . '/' . $hash ['dir1'] . '/' . $hash ['dir2'] . '/' . $hash ['filename']);
        unset ($this->_cc [$key]);
    }
    
    
    /**
     * 删除标签
     */
    public function deleteTag ($tag) {
        if (($t = $this->get ('tag_' . $tag)) != NULL ){
            while (list (, $v) = each ($t))
                $this->delete ($v);
        }
    }
    
    
    /**
     * 根据缓存名称对目录进行hash存放
     *
     * @param $str 缓存名称
     *
     * @return array ('dir1' =>, 'dir2' =>, 'filename' =>);
     */
    private function dirHash ($str) {
        $c = md5 ($str);
        $t = ord ($c{0})*ord ($c{4})+ord ($c{8})*ord ($c{20})+ord ($c{12})*ord ($c{16})+ ord ($c{18}) * ord ($c{24})+ ord ($c{26}) * ord ($c{30});
        $dir1 = ($t / 100 ) % 100;
        $dir2 = $t % 100;
        
        return array ('dir1' => $dir1, 'dir2' => $dir2, 'filename' => base64_encode($str) . '.php');
    }
}